//------------------------------------------------------------------------------
// X2D Game Engine
// Copyright (C) 2006, 2007, 2008 Janusz Kowalski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef X2D_VIEW_H
#define X2D_VIEW_H 1

#include "SDL.h"

//#include "imgSDL.h"
#include "x2tk_Widget.h"
#include "x2d_New.h"

namespace x2d {

const int VIEW_NOFOLLOW = 0;
const int VIEW_XFOLLOW = 1;

// Object of the class View is a widget showing objects of the class World.
class View : public x2tk::Widget {
  public:
  private:
    // Velocity (in map pixels per milisecond).
    float velX_, velY_;
    // Acceleration (in map pixels per square milisecond).
    float accelX_, accelY_;
    // Entity to follow
    Entity *entityToFollow;
    // Visibility of the planes.
    bool planeVisible_[MAP_MAX_D];
    
    int targetX_;
    int targetY_;
    
    int followMode_;
  protected:
    // Displacement of the View.
    float dx, dy;

    World *world_;
    
    
  public:
//     int getVShift(int layer) const { return ((layer&1)==1?16:0); }
//     int getHShift(int layer) const { return ((layer&1)==1?16:0); }
    int getVShift(int mx, int my, int mz) const;
    int getHShift(int mx, int my, int mz) const;
  
    void drawMapPlane(GraphicContext &g, int plane);
  
    // Constructor of the view.
    View(int x, int y, int w, int h, World *world);
    
    // Functions used to change the velocity of the View.
    inline void setVelocityX(float velX) {velX_ = velX;}
    inline void setVelocityY(float velY) {velY_ = velY;}
    
    // Moves the View. Should be called in equal time intervals.
    virtual void move();
    
    virtual void draw(GraphicContext &g);
    
    // Choose entity to follow; 0 - do not follow anything.
    void follow(Entity *ent);
    
    // Access to the world
    inline World *getWorld() {return world_;}
    inline float getDeltaX() {return dx;}
    inline float getDeltaY() {return dy;}
    
    // Look at the given position of the map.
    void lookAt(int x, int y);
    
    inline void incTargetX(int v) {targetX_ +=v;}
    void incTargetY(int v) {targetY_ += v;}
    
    void setFollowMode(int mode);
    
    virtual void event(x2tk::EV ev);
};

struct XYZV {
  int x,y,z,v;
  bool operator==(const XYZV &b) {
    if(x==b.x && y==b.y && z==b.z && v==b.v) return true;
    else return false;
  }
  XYZV& operator=(const XYZV &b) {
    x=b.x; y=b.y; z=b.z; v=b.v;
  }
};

struct IntXY {
  int x;
  int y;
  IntXY(int x_, int y_) : x(x_), y(y_) { }
  double r(IntXY &xy) {
    int dx=xy.x-x, dy=xy.y-y;
    return sqrt(double(dx*dx+dy*dy));
  }

  inline IntXY operator- (IntXY other) {
    x-=other.x; y-=other.y; return *this;
  }

  inline int operator* (IntXY other) {
    return x*other.x+y*other.y;
  }
};

class EditView : public View {
  private:
    unsigned formId_;
    
    int currentPlane_;
    Image imgGrid_;
    Image imgDiag_;
    
    bool selMode_;
    // selW_ and selH_ may be negative.
    int selX_, selY_, selW_, selH_;
    RawVector<int> selBuffer_;
    
    bool pasteMode_;
    int pasteX_, pasteY_;
    
    RawVector< RawVector<XYZV> > history_;
    
    bool history_new;
    
    void history_add(int x, int y, int plane, int v);
    void history_undo();
    void history_group();
    
    void edit_paste(int mapX, int mapY);
    
    using View::getVShift; using View::getHShift;
//     int getVShift() const {return getVShift(currentPlane_);};
//     int getHShift() const {return getHShift(currentPlane_);};
    
    enum EditMode { EM_TEST, EM_CONVEX, EM_NONE };
    
    EditMode editMode_;
    
    std::vector<IntXY> vertList_;
    bool isCCW;

    // True if the mouse pointer is inside a convex being edited.
    // Valid only if there is such convex (vertList_.size()>2).
    bool inside;
    
    // DrawPolyObject for polygon.
    DrawPolyObject *defaultDrawPolyObject_;
  public:
    EditView(int x, int y, int w, int h, World *world);
    ~EditView();
    bool mouseDown(int type);
    void mouseUp(int type);
    void move();
    virtual void draw(GraphicContext &g);
    virtual void event_key(const SDL_KeyboardEvent& key);
    inline void setCurrentPlane(int plane) {currentPlane_ = plane;}
    
    void undo() {history_undo();}
    void clearHistory() {history_.clear();}


  private:
    int editPointX_, editPointY_;
};

// !! plEnt_ should have some value assigned at the beginning.

class GameView : public View {
  private:
    PlayerEnt *plEnt_;
  public:
    GameView(int x, int y, int w, int h, World *world);
    virtual void event_key(const SDL_KeyboardEvent& key);
    bool mouseDown(int type);
    void setPlEnt(PlayerEnt *plEnt) {
      plEnt_=plEnt;
    }
};

} /* namespace x2d */

#endif

